﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using AMM.Model;
using AMM.Model.Store;

namespace AMM.DataAccess.Store {
    public class OutsourceOrderRepository : BaseRepository<OutsourceOrder> {

        private int _pageSize;

        public OutsourceOrderRepository(int pageSize) {
            this._pageSize = pageSize;
        }

        public OutsourceOrder FetchOutsourceOrderById(int outsourceOrderId) {
            using (var ctx = this.Context) {
                return ctx.OutsourceOrders
                    .Include("OutsourceOrderDetails")
                    .FirstOrDefault(x => x.OutsourceOrderId == outsourceOrderId);
            }
        }

        public OutsourceOrder FetchOutsourceOrderByProductConductId(int productConductId) {
            using (var ctx = this.Context) {
                return ctx.OutsourceOrders
                    .Include("Vendor")
                    .Include("OutsourceOrderDetails")
                    .FirstOrDefault(x => x.OutsourceOrderDetails.Any(y => y.ProduceConductId == productConductId));
            }
        }

        public OutsourceOrder FetchOutsourceOrderByIdForView(int outsourceOrderId) {
            using (var ctx = this.Context) {
                return ctx.OutsourceOrders
                    .Include("OutsourceOrderDetails")
                    .Include("OutsourceOrderDetails.ProduceConduct")
                    .Include("OutsourceOrderDetails.ProduceConduct.Product")
                    .FirstOrDefault(x => x.OutsourceOrderId == outsourceOrderId);
            }
        }

        public void UpdateOutsourceOrder(OutsourceOrder outsourceOrder) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        outsourceOrder.OutsourceOrderDetails.Where(x => x.ClassStatus == ClassStatus.Added).ToList().ForEach(x => {
                            x.OutsourceOrder = outsourceOrder;
                            x.OutsourceOrderId = outsourceOrder.OutsourceOrderId;
                            x.OutsourceOrderDetailsId = Guid.NewGuid().GetHashCode();
                        });
                        // do not try to merge with above codes, because you can't change the fields which was marked as primary key
                        outsourceOrder.OutsourceOrderDetails.ToList().ForEach(x => {
                            ctx.OutsourceOrderDetails.Attach(x);
                            switch (x.ClassStatus) {
                                case ClassStatus.UnChanged:
                                    ctx.OutsourceOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Unchanged;
                                    break;
                                case ClassStatus.Updated:
                                    x.ModifiedBy = outsourceOrder.ModifiedBy;
                                    x.ModifiedDatetime = DateTime.Now;
                                    ctx.OutsourceOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Modified;
                                    break;
                                case ClassStatus.Added:
                                    x.CreatedBy = outsourceOrder.ModifiedBy;
                                    x.CreatedDatetime = DateTime.Now;
                                    ctx.OutsourceOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Added;
                                    break;
                                case ClassStatus.Deleted:
                                    ctx.OutsourceOrderDetails.Attach(x);
                                    ctx.Entry(x).State = EntityState.Deleted;
                                    break;
                            }
                        });

                        ctx.OutsourceOrders.Attach(outsourceOrder);
                        ctx.Entry(outsourceOrder).State = EntityState.Modified;
                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public void DeleteMultipleOutsourceOrder(params int[] outsourceOrderIds) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        ctx.OutsourceOrderDetails.Where(x => outsourceOrderIds.Contains(x.OutsourceOrderId)).ToList().ForEach(x => {
                            ctx.OutsourceOrderDetails.Remove(x);
                        });
                        ctx.OutsourceOrders.Where(x => outsourceOrderIds.Contains(x.OutsourceOrderId)).ToList().ForEach(x => {
                            ctx.OutsourceOrders.Remove(x);
                        });
                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }

        public List<OutsourceOrder> FetchOutsourceOrderList(OutsourceOrderSearchModel searchModel, int pageIndex) {
            using (var ctx = this.Context) {
                var query = ctx.OutsourceOrders
                    .Include("Vendor")
                    .Include("Vendor.User")
                    .Include("OutsourceOrderDetails")
                    .Include("OutsourceOrderDetails.ProduceConduct")
                    .Include("OutsourceOrderDetails.ProduceConduct.Product")
                    .Where(x => !string.IsNullOrEmpty(x.Code));

                if (!string.IsNullOrEmpty(searchModel.Code)) {
                    query = query.Where(x => x.Code.Contains(searchModel.Code));
                }
                if (!string.IsNullOrEmpty(searchModel.ProductCode)) {
                    query = query.Where(x => x.OutsourceOrderDetails.Any(y => y.ProduceConduct.Product.Code.Contains(searchModel.ProductCode)));
                }
                if (!string.IsNullOrEmpty(searchModel.User)) {
                    query = query.Where(x => x.Vendor.User.Name.Contains(searchModel.User));
                }
                if (searchModel.CreateDateStart.HasValue) {
                    query = query.Where(x => x.CreatedDatetime >= DbFunctions.TruncateTime(searchModel.CreateDateStart.Value));
                }
                if (searchModel.CreateDateEnd.HasValue) {
                    query = query.Where(x => x.CreatedDatetime <= DbFunctions.TruncateTime(searchModel.CreateDateEnd.Value));
                }
                if (searchModel.DeliverDateStart.HasValue) {
                    query = query.Where(x => x.OutsourceOrderDetails.Any(y => y.DeliveryDate >= DbFunctions.TruncateTime(searchModel.DeliverDateStart.Value)));
                }
                if (searchModel.DeliverDateEnd.HasValue) {
                    query = query.Where(x => x.OutsourceOrderDetails.Any(y => y.DeliveryDate <= DbFunctions.TruncateTime(searchModel.DeliverDateEnd.Value)));
                }
                if (searchModel.PayCurrencyId.HasValue) {
                    query = query.Where(x => x.Vendor.PayCurrencyId == searchModel.PayCurrencyId.Value);
                }
                if (searchModel.VendorId.HasValue) {
                    query = query.Where(x => x.VendorId == searchModel.VendorId.Value);
                }
                if (searchModel.DeliverStatus.HasValue) {
                    switch (searchModel.DeliverStatus.Value) {
                        case 1: // 已交齐
                            query = query.Where(x => x.OutsourceOrderDetails.Any(y => y.Quantity == y.Quantity));
                            break;
                        case 2: // 已交货
                            query = query.Where(x => x.OutsourceOrderDetails.Any(y => y.Quantity > 0));
                            break;
                        case 3: // 未交货
                            query = query.Where(x => x.OutsourceOrderDetails.Any(y => y.Quantity == 0));
                            break;
                        case 4: // 已交货 && 未交货
                            query = query.Where(x => x.OutsourceOrderDetails.Any(y => y.Quantity < y.Quantity));
                            break;
                    }
                }
                if (searchModel.OrderStatus.HasValue) {
                    query = query.Where(x => x.Status == searchModel.OrderStatus.Value);
                }
                if (searchModel.PrintStatus.HasValue) {
                    // 1: 已打印 2: 未打印
                    query = query.Where(x => x.IsPrinted == (searchModel.PrintStatus.Value == 1));
                }

                searchModel.Records = query.Count();
                pageIndex = (pageIndex - 1) * this._pageSize > searchModel.Records ? 1 : pageIndex;

                return query.OrderByDescending(x => x.CreatedDatetime).Skip((pageIndex - 1) * this._pageSize).Take(this._pageSize).ToList();
            }
        }

        public int GetTheMaxFlowNumber(string startString) {
            using (var ctx = this.Context) {
                OutsourceOrder outsourceOrder = ctx.OutsourceOrders
                    .Where(x => x.Code.StartsWith(startString))
                    .OrderByDescending(x => x.Code)
                    .FirstOrDefault();
                int maxFlowNumber = outsourceOrder == null
                    ? 0
                    : Convert.ToInt32(outsourceOrder.Code.Substring(outsourceOrder.Code.Length - 2, 2));
                return Math.Max(ctx.OutsourceOrders.Count(x => x.Code.StartsWith(startString)), maxFlowNumber);
            }
        }

        public void SaveGeneratedProdurementOrder(List<OutsourceOrder> outsourceOrderList,
            List<ProduceConduct> produceConductList) {
            using (var ctx = this.Context) {
                using (DbContextTransaction trans = ctx.Database.BeginTransaction()) {
                    try {
                        outsourceOrderList.ForEach(x => {
                            ctx.OutsourceOrders.Attach(x);
                            ctx.Entry(x).State = EntityState.Added;
                            x.OutsourceOrderDetails.ToList().ForEach(y => {
                                ctx.OutsourceOrderDetails.Attach(y);
                                ctx.Entry(y).State = EntityState.Added;
                            });
                        });
                        produceConductList.ForEach(x => {
                            ctx.ProduceConducts.Attach(x);
                            ctx.Entry(x).State = EntityState.Modified;
                        });

                        ctx.SaveChanges();
                        trans.Commit();
                    }
                    catch (Exception ex) {
                        trans.Rollback();
                        throw ex;
                    }
                }
            }
        }        
    }
}
